<template>
  <div class="comment">

    <div class="comment-box" :class="focus_state?'input_focus':'input_no_focus'" >

      <div class="mentions-box " >
        <div class="content" @click.prevent="setFocus">
          <!--文本框-->
          <div ref="divRef" class="editor" contenteditable  @keyup="handleKeyUp" @keydown="handleKeyDown"/>
          <div style="position: absolute;z-index: 1;top: 0px;left: 10px;height: 22px;color: #999999" v-if="comment_word.length==0"  @click="setFocus">添加评论,可@相关人员</div>
          <!--选项-->
          <mention-user v-if="showDialog" :mock-data="optionsList" :visible="showDialog" :position="position" :query-string="queryString"
                        @onPickUser="handlePickUser" @onHide="handleHide" @onShow="handleShow" />
        </div>
      </div>

      <div v-if="fileList.length" class="comment-files">
        <a-row :gutter="[12,12]" :wrap="true">
          <a-col :span="12" v-for="(item,index) in fileList" :key="index" >
            <div class="flex justify-between align-center comment-file-item">
              <div>{{item.name}}</div>
              <delete-outlined  class="close-icon" style="color: red" @click="deleteFile(index)"/>
            </div>
          </a-col>
        </a-row>
      </div>
      <div v-if="imageList.length" class="comment-images">
        <div v-for="(item,index) in imageList" :key="index" class="comment-img-box">
          <a-image
              :width="32"
              :src="item.url"
          />
          <close-circle-filled class="close-icon" @click="deleteImage(index)"/>
        </div>
      </div>


        <a-upload
            class="file-upload"
            v-model:file-list="fileList"
            :action="$utils.fileAction"
            :headers="{ 'org-token': $store.getters.token }"
            @change="handleFileUploadChange"
            name="file"
            :data="{ dir: 'approval_file' }"
            accept=".doc,.docx,.pdf,.xls,.xlsx,.jpg,.jpeg,.png"
            list-type="picture"
            :max-count="9"
            :showUploadList="false"
        >
          <paper-clip-outlined   class="gray-icon-color"  />
        </a-upload>

        <a-upload
            class="img-upload"
            v-model:file-list="imageList"
            :action="$utils.imgAction"
            :headers="{ 'org-token': $store.getters.token }"
            @change="handleUploadChange"
            name="image"
            :data="{ dir: 'approval_image' }"
            accept="image/*"
            list-type="picture"
            :max-count="9"
            :showUploadList="false"
        >

          <picture-outlined  class="gray-icon-color" />
        </a-upload>


    </div>

    <div class="actions text-right" v-if="!(comment_word.length==0&&comment_images.length==0&&comment_files.length==0)">
      <a-space>
        <a-button type="primary" ghost @click="handleCancel">取消</a-button>
        <a-button type="primary"  @click="handleCommit">发送</a-button>
      </a-space>
    </div>
  </div>

</template>

<script>
import request from '@/common/utils/request'
import {computed, onMounted, reactive, toRefs, ref, watch} from 'vue'
import {CloseCircleFilled, PaperClipOutlined, PictureOutlined, DeleteOutlined  } from '@ant-design/icons-vue'
import {message} from 'ant-design-vue'
import _ from "lodash"
import MentionUser from './MentionUser.vue'

export default {
  components: {PictureOutlined, CloseCircleFilled, PaperClipOutlined, DeleteOutlined,MentionUser},
  props:{
    approvalId:{
      type:[String,Number],
      default:0
    },
    pid:{
      type:[String,Number],
      default:0
    },
    list:{
      type:Array,
      default: ()=>([])
    },
    filePreviewUrl:{
      type:String,
      default:""
    },

  },
  setup(props,{emit}) {
    const state = reactive({
      focus_state:false,
      comment_word:'',
      comment_files:[],
      comment_images:[],
      mention_user:[], //@的人
      optionsList:[],
    })
    const getDepartmentList = () => {
      request
          .get("/org", "/department/index", { withUser: 1, status: 4 })
          .then((res) => {
            console.log(res)
            state.optionsList = res.list[0].personal.map((item) => ({
              name: item.realname,
              id: item.uid+"",
            }));

          });
    };

    const downloadFile = (file) => {
      window.microApp.forceDispatch(
          {
            type: "previewFile",
            visible: true,
            file,
            filePreviewUrl:props.filePreviewUrl
          }
      );
    };

    const mentionSelect = (option) => {
      const item = state.optionsList.find(it=>{
        return it.value==option.value
      })
      state.comment_word = state.comment_word.substring(0,state.comment_word.length-option.value.length-1)
      state.comment_word = state.comment_word +item.label+ " "
      state.mention_user.push(item)
      //数组去重
      const uniqueArray = _.uniqWith(state.mention_user, (a, b) => a.value === b.value)
      state.mention_user = uniqueArray
      console.log(state.mention_user,"mention_user")
    }

    // 附件
    const fileList = ref([]);
    let files = [];
    const handleFileUploadChange = (info) => {
      let resFileList = [...info.fileList];
      if (resFileList.length) {
        resFileList = resFileList.map((file) => {
          if (file.response) {
            if (file.response.code == 1000) {
              file.url = file.response.data.fileUrl;
              files.push({
                url:file.url,
                name:file.name,
              });
            } else {
              message.warning(file.response.msg);
            }
          }
          return file;
        });
      } else {
        images = [];
      }
      fileList.value = resFileList;
      setCommentFiles()
    };

    // 图片
    const imageList = ref([]);
    let images = [];
    const handleUploadChange = (info) => {
      let resFileList = [...info.fileList];
      if (resFileList.length) {
        resFileList = resFileList.map((file) => {
          if (file.response) {
            if (file.response.code == 1000) {
              file.url = file.response.data.fileUrl;
              images.push({url:file.url,
                name:file.name||file.url,});
            } else {
              message.warning(file.response.msg);
            }
          }
          return file;
        });
      } else {
        images = [];
      }
      imageList.value = resFileList
      setCommentImages()
    };

    const setCommentFiles = () => {
      state.comment_files = fileList.value.map(item=>{
        return  {
          url:item.url,
          name:item.name
        }
      })
    }
    const deleteFile = (index) => {
      fileList.value.splice(index,1)
    }
    const setCommentImages = () => {
      state.comment_images = imageList.value.map(item=>{
        return {
          url:item.url,
          name:item.name
        }
      })
    }
    const deleteImage = (index) => {
      imageList.value.splice(index,1)
      setCommentImages()
    }
    const reSet = () => {
      state.focus_state = false
      state.comment_images = []
      imageList.value = []
      state.comment_files = []
      fileList.value = []
      state.comment_word = ""
      divRef.value.innerHTML = ""

    }

    const handleCancel = () => {
      //取消
      reSet()
    }

    const handleCommit = () => {
      const  comment_word_arr = getMentionArr()
      state.comment_word = divRef.value.innerText;
      //提交
      const comment = {
        comment_word:state.comment_word,
        comment_files:state.comment_files,
        comment_images:state.comment_images,
      }
      if (comment_word_arr.length){
        comment.comment_word_arr = comment_word_arr
        const mentions =comment_word_arr.filter(item=>{
          return item.id
        })
        comment.mention_user = mentions.map(item=>{
          return {
            label:item.str,
            value:item.id
          }
        })
      }
      const params = {
        "comment":comment,
        id:props.approvalId
      }
      if (props.pid){
        params.pid = Number(props.pid)
      }
      console.log(params,"提交的参数")
      request.post("/work_approval","/manage/approval/form/commentApproval",params).then(res=>{
        console.log(res)
        reSet()
        emit("success")
      })


    }

    onMounted(()=>{
      getDepartmentList()
      state.focus_state = false
    })

    const userList = ref([]) //用户数据列表
    const node = ref('')  // 获取到节点
    const user = ref('')  // 选中项的内容
    const endIndex = ref('')   // 光标最后停留位置
    const queryString = ref('')  // 搜索值
    const showDialog = ref(false)   // 是否显示弹窗
    const position = ref({
      x: 0,
      y: 0
    })  // 弹窗显示位置
    const divRef = ref(null)


// 数组去重
    const distinct = (arr, key) => {

      var newObj = {}

      var newArr = []

      for (var i = 0; i < arr.length; i++) {

        var item = arr[i]

        if (!newObj[item[key]]) {
          newObj[item[key]] = newArr.push(item)
        }
      }
      return newArr
    }

//格式化

    const escape2Html = (str) => {

      // 格式化
      var arrEntities = { lt: '<', gt: '>', nbsp: ' ', amp: '&', quot: '"' }
      return str.replace(/&(lt|gt|nbsp|amp|quot);/gi, function (all, t) {
        return arrEntities[t]
      })

    }

//获取输入的@用户的信息
    const getMentionArr = () => {

      const text = divRef.value.innerHTML
      const text2 = escape2Html(text)
      let list = []
      for (let index = 0; index < userList.value.length; index++) {

        if (text2.includes(`"id":"${userList.value[index].id}"`)) {
          list.push(userList.value[index])
        }
      }
      const indexs = []
      for (let i = 0; i < list.length; i++) {
        console.log(list[i])
        let getString = `@${list[i].name}\u200b`
        const idnexxx = searchSubStr(divRef.value.innerText,getString)
        for (let j = 0; j <idnexxx.length ; j++) {
          indexs.push({
            start:idnexxx[j],
            length:getString.length,
            str:getString,
            id:list[i].id,
          })
        }
      }
      list = distinct(list, 'id')

      console.log(divRef.value.innerText, list)

      let lists = []
      console.log(indexs,"indexs")
      let indexsssss = _.uniqBy(indexs, 'start')
      indexsssss = _.sortBy(indexsssss, 'start')
      let stepIndex = 0
      console.log(indexsssss,"indexs")
      console.log(stepIndex,"stepIndex")
      for (let i = 0; i < indexsssss.length; i++) {
        const  item = indexsssss[i]
        console.log(item,"xxxxx")
        if (i==0){
          if (item.start==0){
            lists.push({
              str: item.str,
              type:"at",
              id:item.id,
            })
            stepIndex =stepIndex+ item.str.length
          }else {
            let  subStr = divRef.value.innerText.substring(stepIndex,item.start-1)
            if (subStr.length){
              lists.push({
                str:subStr,
                type:"str",
                id:item.id,
              })
              stepIndex = stepIndex+subStr.length
            }

            lists.push({
              str: item.str,
              type:"at",
              id:item.id,
            })
            stepIndex = stepIndex+item.str.length +1
          }

        }else {
          stepIndex = stepIndex+1

          let  subStr = divRef.value.innerText.substring(stepIndex,item.start)
          if (subStr.length==0){
            lists.push({
              str: item.str,
              type:"at",
              id:item.id,
            })
            stepIndex = stepIndex+ item.str.length

          }else {

            if (subStr.length){
              lists.push({
                str:subStr,
                type:"str",
                id:item.id,
              })
              stepIndex = stepIndex+subStr.length
            }
            lists.push({
              str: divRef.value.innerText.substring(stepIndex,stepIndex+item.str.length),
              type:"at",
              id:item.id,
            })
            stepIndex = stepIndex+item.str.length
          }
        }
      }
      if (stepIndex< divRef.value.innerText.length-1){
        lists.push({
          str:divRef.value.innerText.substring(stepIndex,divRef.value.innerText.length-1),
          type:"str",
        })
      }
      return  lists.filter(item=>{
        return item.str.length!=0
      })

    }

    const searchSubStr=(str,subStr)=>{
      const positions = []
      var pos = str.indexOf(subStr);
      console.log(str,subStr)
      console.log(pos)
      while(pos>-1){
        positions.push(pos);
        pos = str.indexOf(subStr,pos+1);
      }
      return positions;
    }

//初始化
    onMounted(() => {
      handleIn()
    })



//初始化各项数据

    const handleIn = async () => {

      // divRef.value.focus()

      document.execCommand('selectAll', false, '')

      let a = document.getSelection()

      a.collapseToEnd()

      const node1 = getRangeNode()

      const endIndex1 = getCursorIndex()

      node.value = node1

      endIndex.value = endIndex1

      position.value = await getRangeRect() //需要异步获取
      queryString.value = getAtUser() || ''
      showDialog.value = false

    }

// 获取光标位置

    const getCursorIndex = () => {

      const selection = window.getSelection()//窗口的selection对象，当前用户选择的文本

      return selection.focusOffset // 选择开始处 focusNode 的偏移量

    }

// 获取节点

    const getRangeNode = () => {
      const selection = window.getSelection()
      return selection.focusNode // 选择的束节点，正在聚焦的节点
    }

    const setFocus = () => {
      state.focus_state = true
      divRef.value.focus()
    }

// 弹窗出现的位置
    const getRangeRect = async () => {

      const selection = (window).getSelection()
      const range = selection.getRangeAt(0) // 是用于管理选择范围的通用对象
      const rect = await range.getClientRects()[0] // 择一些文本并将获得所选文本的范围
      // const rect = range.getBoundingClientRect()

      const LINE_HEIGHT = 30
      if (rect === undefined) { //光标在行首，是undefined
        divRef.value.innerHTML = '\u200b' //零字符
        const rect = range.getClientRects()[0]
        let obj = { x: 0, y: 0 }
        return obj

      } else {
        return {
          x: rect.x,
          y: rect.y + LINE_HEIGHT - 40   //上移
        }
      }

    }

// 是否展示 @匹配@

    const showAt = () => {

      const node = getRangeNode() //获取当前被聚焦的节点

      if (!node || node.nodeType !== Node.TEXT_NODE) return false

      const content = node.textContent || '' //获取节点文本

      const regX = /@([^@\s]*)$/

      const match = regX.exec(content.slice(0, getCursorIndex())) //输入到 当前光标位置 匹配@

      return match && match.length === 2  //返回@符号 一个@长度为2

    }

// 获取 @ 用户 @后的数据

    const getAtUser = () => {
      const content = getRangeNode().textContent || ''
      const regX = /@([^@\s]*)$/
      const match = regX.exec(content.slice(0, getCursorIndex()))
      if (match && match.length === 2) {
        return match[1] //@ 后面跟着的字符 光标停留
      }
      return undefined

    }

// 创建整个@标签

    const createAtButton = (user) => {

      const btn = document.createElement('span')

      btn.style.display = 'inline-block'

      btn.dataset.user = JSON.stringify(user)

      btn.className = 'at-button'

      btn.contentEditable = 'false'

      btn.textContent = `@${user.name}\u200b`

      btn.style.color = '#2681F3'

      const wrapper = document.createElement('span')

      wrapper.style.display = 'inline-block'

      wrapper.contentEditable = 'false'

      const spaceElem = document.createElement('span')

      spaceElem.style.whiteSpace = 'pre'
      spaceElem.textContent = '\u200b'
      spaceElem.contentEditable = 'false'
      const clonedSpaceElem = spaceElem.cloneNode(true)
      wrapper.appendChild(spaceElem)
      wrapper.appendChild(btn)
      wrapper.appendChild(clonedSpaceElem)
      return wrapper

    }

    const replaceString = (raw, replacer) => {
      return raw.replace(/@([^@\s]*)$/, replacer)
    }


// 插入@标签
    const replaceAtUser = (user) => {
      const node1 = node.value

      if (node1 && user) {
        const content = node1.textContent || ''
        const endIndex1 = endIndex.value
        const preSlice = replaceString(content.slice(0, endIndex1), '')
        const restSlice = content.slice(endIndex1)
        const parentNode = node1.parentNode
        const nextNode = node1.nextSibling
        const previousTextNode = new Text(preSlice)
        const nextTextNode = new Text('\u200b' + restSlice) // 添加 0 宽字符
        const atButton = createAtButton(user) //创建@user标签
        parentNode.removeChild(node1)
        // 插在文本框中
        if (nextNode) {
          parentNode.insertBefore(previousTextNode, nextNode)
          parentNode.insertBefore(atButton, nextNode)
          parentNode.insertBefore(nextTextNode, nextNode)
        } else {
          parentNode.appendChild(previousTextNode)
          parentNode.appendChild(atButton)
          parentNode.appendChild(nextTextNode)
        }
        // 重置光标的位置
        const range = new Range()
        const selection = window.getSelection()
        range.setStart(nextTextNode, 0)
        range.setEnd(nextTextNode, 0)
        selection.removeAllRanges()
        selection.addRange(range)
      }
    }

// 键盘抬起事件
    const handleKeyUp = async () => {

      state.comment_word = divRef.value?.innerText
      //匹配@ 出现@
      if (showAt()) {
        const node1 = getRangeNode()
        const endIndex1 = getCursorIndex() //获取光标位置
        node.value = node1
        endIndex.value = endIndex1
        position.value = await getRangeRect() //获取坐标需要时间，需要异步获取
        queryString.value = getAtUser() || ''
        showDialog.value = true
      } else {
        showDialog.value = false
      }
      // 限制长度 为1000 截取
      if (divRef.value.innerText.length > 1000) {
        divRef.value.innerText = divRef.value.innerText.substr(
            0,
            1000
        )
        // 光标移动到最后
        document.execCommand('selectAll', false, '')
        //将选种网页中的全部内容，也就是全部文本,第二个参数为true，会显示对话框
        let a = document.getSelection()
        a.collapseToEnd()
      }

    }

// 键盘按下事件

    const handleKeyDown = (e) => {

      //当前@选择被打开
      if (showDialog.value) {
        if (e.code === 'ArrowUp' || e.code === 'ArrowDown' || e.code === 'Enter') {
          // 阻止浏览器默认动作 (页面跳转)
          e.preventDefault()
        }
      }
    }

// 插入标签后 隐藏选择框  获取子组件抛出的user
    const handlePickUser = (user1) => {
      userList.value.push(user1)
      replaceAtUser(user1)
      user.value = user1
      showDialog.value = false

      state.comment_word = divRef.value.innerText
    }

// 隐藏选择框
    const handleHide = () => {
      showDialog.value = false
    }

// 显示选择框
    const handleShow = () => {
      showDialog.value = true
    }

    return {
      ...toRefs(state),
      fileList,
      imageList,
      handleUploadChange,
      handleFileUploadChange,
      deleteImage,
      deleteFile,
      handleCancel,
      handleCommit,
      mentionSelect,

      handleKeyUp,
      handleKeyDown,
      showDialog,
      position,
      queryString,
      handlePickUser,
      handleHide,
      handleShow,
      divRef,
      setFocus,




    }
  }
}
</script>

<style lang="less" scoped>
.comment{

  padding:12px 0px;

  .comment-box{
    position: relative;
    border-radius: 10px;
    padding: 10px;
    margin-bottom: 10px;

    .mentions-box{
      textarea{
        padding: 10px;
      }
    }


    .comment-files{
      padding: 13px 10px 0;
      .comment-file-item{
        padding: 5px 10px;
        border-radius: 5px;
        border: 1px solid #eeeeee;
      }
    }
    .comment-images{
      height: 45px;
      padding: 13px 10px 0;
      overflow: hidden;

      .comment-img-box{
        position: relative;
        display: inline-block;
        margin-right: 12px;
        width: 32px;
        height: 32px;
        border-radius: 10px;

        .close-icon{
          position: absolute;
          width: 20px;
          height: 20px;
          top: -5px;
          right: -10px;
        }
      }

    }

    .img-upload{
      position: absolute;
      display: inline-block;
      bottom: 8px;
      right: 10px;
      width: 30px;
      height: 30px;
      line-height: 30px;
      text-align: center;
      border-radius: 5px;
    }
    .file-upload{
      position: absolute;
      right: 40px;
      bottom: 8px;
      display: inline-block;
      width: 30px;
      height: 30px;
      line-height: 30px;
      text-align: center;
      border-radius: 5px;
    }


  }
}

.img-upload-white{
  bottom:15px;
  right: 16px;
  background-color: transparent;
}
.img-upload-grey{
  bottom:10px;
  right: 10px;
  background-color: #DCDCDD;
}


.input_focus{
  border: 1px solid #2681f3;
  background: white;
}
.input_no_focus{
  border: 1px solid #eeeeee;
  background: white;
}

.gray-icon-color{
  color: #646A73;
  width: 18px;
  height: 18px;
  font-size: 18px;
  margin-top: 7px;
  cursor: pointer;
}

:deep(.ant-mentions){
  border: 0 solid #ccc !important;
  border-right-width: 0 !important;
}
:deep(.ant-mentions:hover){
  border-right-width: 0 !important;
  border-right: 0 solid #cccccc;
}
:deep(.ant-mentions-focused){
  box-shadow: none !important;
  border-right-width: 0 !important;
}


.content {
  position: relative;
  width: 100%;
  font-family: sans-serif;
  border-color: white;

  h1 {
    border-color: white;
    text-align: center;
  }

}

.editor {
  margin: 0 auto;

  width: 100%;

  background: #fff;

  border-color: white;
  //border: 1px solid #ccc;

  border-radius: 5px;

  text-align: left;

  padding: 0 10px;

  overflow: auto;

  scroll-behavior: smooth;

  &-focus {

    //outline: none;

  }

}

[contenteditable] {
  outline: 0px solid transparent;
}

</style>